#ifndef __VIRTIO_H__
#define __VIRTIO_H__

#include "type.h"

//
// virtio device definitions.
// for both the mmio interface, and virtio descriptors.
// only tested with qemu.
// this is the "legacy" virtio interface.
//
// the virtio spec:
// https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf
//

// virtio mmio control registers, mapped starting at 0x10001000.
// from qemu virtio_mmio.h
#define VIRTIO_MMIO_MAGIC_VALUE 0x000  // 0x74726976
#define VIRTIO_MMIO_VERSION 0x004      // version; 1 is legacy
#define VIRTIO_MMIO_DEVICE_ID 0x008    // device type; 1 is net, 2 is disk
#define VIRTIO_MMIO_VENDOR_ID 0x00c    // 0x554d4551
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
#define VIRTIO_MMIO_GUEST_PAGE_SIZE 0x028   // page size for PFN, write-only
#define VIRTIO_MMIO_QUEUE_SEL 0x030         // select queue, write-only
#define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034     // max size of current queue, read-only
#define VIRTIO_MMIO_QUEUE_NUM 0x038         // size of current queue, write-only
#define VIRTIO_MMIO_QUEUE_ALIGN 0x03c       // used ring alignment, write-only
#define VIRTIO_MMIO_QUEUE_PFN 0x040         // physical page number for queue, read/write
#define VIRTIO_MMIO_QUEUE_READY 0x044       // ready bit
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050      // write-only
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060  // read-only
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064     // write-only
#define VIRTIO_MMIO_STATUS 0x070            // read/write

// status register bits, from qemu virtio_config.h
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
#define VIRTIO_CONFIG_S_DRIVER 2
#define VIRTIO_CONFIG_S_DRIVER_OK 4
#define VIRTIO_CONFIG_S_FEATURES_OK 8

// device feature bits
#define VIRTIO_BLK_F_RO 5          /* Disk is read-only */
#define VIRTIO_BLK_F_SCSI 7        /* Supports scsi command passthru */
#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */
#define VIRTIO_BLK_F_MQ 12         /* support more than one vq */
#define VIRTIO_F_ANY_LAYOUT 27
#define VIRTIO_RING_F_INDIRECT_DESC 28
#define VIRTIO_RING_F_EVENT_IDX 29

// this many virtio descriptors.
// must be a power of two.
#define VIRTIO_NUM 8
typedef struct VRingDesc VRingDesc;
typedef struct VRingUsedElem VRingUsedElem;
typedef struct UsedArea UsedArea;

struct VRingDesc {
    uint64_t addr;
    uint32_t len;
    uint16_t flags;
    uint16_t next;
};
#define VRING_DESC_F_NEXT 1   // chained with another descriptor
#define VRING_DESC_F_WRITE 2  // device writes (vs read)

struct VRingUsedElem {
    uint32_t id;  // index of start of completed descriptor chain
    uint32_t len;
};

// for disk ops
#define VIRTIO_BLK_T_IN 0   // read the disk
#define VIRTIO_BLK_T_OUT 1  // write the disk

struct UsedArea {
    uint16_t flags;
    uint16_t id;
    VRingUsedElem elems[VIRTIO_NUM];
};

void virtio_disk_init(void);
void virtio_disk_rw(uint8_t* b,uint32_t secNo, bool write);
void virtio_disk_intr(void);

#endif